package pw.hais.dao;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;

import pw.hais.annotation.Id;
import pw.hais.annotation.NoDB;
import pw.hais.annotation.Table;
import pw.hais.db.DBconn;

/**
 * 基本 数据库操作类
 * @author Hello_海生
 * @date 2014年7月24日
 */
public class BaseDao<T> {
	protected Connection conn=null;		//数据库连接
	protected PreparedStatement pstm=null;
	protected ResultSet rs=null;
	
	protected Class<?> clazz;	//对象类
	protected String tableName = "";		//实体类所对应的表名
	protected String tableId = "";			//实体类所对应的表主键
	protected Field attr[] = null;			//实体类的属性
	
	
	@SuppressWarnings("unchecked")
	protected BaseDao() {
		if(conn==null)conn=new DBconn().getConn(); 	//获取数据库连接
		//获取 泛型 实体
		ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
		clazz = (Class<T>) type.getActualTypeArguments()[0];
		attr = clazz.getDeclaredFields();	//获取实体类属性
		tableName = getTableName();			//获取表名
		tableId = getTableId();				//获取主键
	}	
	
	/**
	 * 自动建表
	 * @param classPath	类 名
	 */
	public BaseDao(String classPath) {
		try {
			clazz = Class.forName(classPath);
			if(conn==null)conn=new DBconn().getConn(); 	//获取数据库连接
			attr = clazz.getDeclaredFields();	//获取实体类属性
			tableName = getTableName();			//获取表名
			tableId = getTableId();				//获取主键
			createTable();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 根据    sql语句 执行 查询
	 * @param sql
	 * @return
	 */
	public ResultSet executeQuery(String sql){
		try {
			if(conn.isClosed())conn=new DBconn().getConn(); 	//获取数据库连接
			//执行操作
			pstm=conn.prepareStatement(sql);
			rs = pstm.executeQuery();
		} catch (Exception e) {
			System.out.println("--执行查询语句出错。"+sql);
		}finally{
			close();
		}
		
		return rs;
	}
	
	/**
	 * 根据    sql语句 执行 更新语句
	 * @param sql
	 * @return
	 */
	public int executeUpdate(String sql){
		int res=0;
		try {
			if(conn.isClosed())conn=new DBconn().getConn(); 	//获取数据库连接
			//执行操作
			pstm=conn.prepareStatement(sql);
			res = pstm.executeUpdate();
		} catch (Exception e) {
			System.out.println("--执行查询语句出错。"+sql);
		}finally{
			close();
		}
		return res;
	}
	
	/**
	 * 根据条件修改内容
	 * @param conditions	条件
	 * @return
	 */
	public int update(T entity,String conditions){
		StringBuffer sql = new StringBuffer("update "+tableName+" set ");
		try {
			for (Field field : attr) {
				field.setAccessible(true);
				if(!field.isAnnotationPresent(NoDB.class) && !field.getName().equals("serialVersionUID")){	//判断该字段是否参与操作
					String val = field.get(entity) + "";	//获取值
					if (!"null".equals(val) && !"0".equals(val)) { // 如果值不为空
						sql.append(field.getName() + "= '"+field.get(entity)+"',");
					}
				}
			}
			
			sql.deleteCharAt(sql.length() - 1); // 去掉最後一個逗號
			if (conditions != null && !"".equals(conditions)) {
				sql.append(" where " + conditions);
			}
			if(conn.isClosed())conn=new DBconn().getConn(); 	//获取数据库连接
			pstm=conn.prepareStatement(sql.toString());
			int rs = pstm.executeUpdate();	
			return rs;
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("--修改内容出错："+sql);
		}finally{
			close();
		}
		
		return 0;
	}
	
	/**
	 * 根据ID 查询 实体
	 * @param id
	 * @return
	 */
	public T findEntityById(String id){
		ArrayList<T> list = findEntityList("id = '"+id+"'");	//根据条件查询用户列表
		//if用户存在
		if(list.size()==1){
			return list.get(0);
		}
		return null;
	}
	
	
	/**
	 * 查询内容列表
	 * @param conditions	条件
	 * @return	实体列表
	 */
	@SuppressWarnings("unchecked")
	public ArrayList<T> findEntityList(String conditions){
		ArrayList<T> list = new ArrayList<T>();
		StringBuffer sql = new StringBuffer("select * from "+tableName+" ");
		try {
			if (conditions != null && !"".equals(conditions)) {
				sql.append("where " + conditions);
			}
			if(conn.isClosed())conn=new DBconn().getConn(); 	//获取数据库连接
			pstm = conn.prepareStatement(sql.toString());
			rs = pstm.executeQuery();
			while (rs.next()) {
				T o = (T) clazz.newInstance();	//实例化一个类
				for (Field field : attr) {
					field.setAccessible(true);		//设置属性为可访问
					if(!field.isAnnotationPresent(NoDB.class) && !field.getName().equals("serialVersionUID")){	//判断该字段是否参与操作
						//获取属性名， 首字母转为大写
						String name = field.getName().replaceFirst(field.getName().substring(0, 1), field.getName().substring(0, 1).toUpperCase());
						//根据数据类型调用方法
						switch (field.getType().getName()+"") {
							case "int": 	//整形
								o.getClass().getMethod("set"+name, int.class).invoke(o, rs.getInt(field.getName()));
								break;
							case "boolean": 	//布尔型
								o.getClass().getMethod("set"+name, boolean.class).invoke(o, rs.getBoolean(field.getName()));
							break;
							case "float": 		//浮点型
								o.getClass().getMethod("set"+name, float.class).invoke(o, rs.getFloat(field.getName()));
							break;
							case "double": 		//双精度
								o.getClass().getMethod("set"+name, double.class).invoke(o, rs.getDouble(field.getName()));
							break;
							case "char": 		//字节型
								o.getClass().getMethod("set"+name, char.class).invoke(o, rs.getString(field.getName()));
							break;
							case "long": 		//长整形
								o.getClass().getMethod("set"+name, long.class).invoke(o, rs.getLong(field.getName()));
							break;
							case "blob": 			//二进制
								o.getClass().getMethod("set"+name, Blob.class).invoke(o, rs.getObject(field.getName()));
							break;
							case "java.lang.String": 	//字符串型
								o.getClass().getMethod("set"+name, String.class).invoke(o, rs.getString(field.getName()));
							break;
							case "java.sql.Date": 		//日期
								o.getClass().getMethod("set"+name, Date.class).invoke(o, rs.getObject(field.getName()));
							break;
							case "java.sql.Time": 		//时间
								o.getClass().getMethod("set"+name, Time.class).invoke(o, rs.getObject(field.getName()));
							break;
							case "java.sql.Timestamp": 		//shij
								o.getClass().getMethod("set"+name, Timestamp.class).invoke(o, rs.getObject(field.getName()));
							break;
						}
					}
				}
				list.add(o);
			}
		} catch (Exception e) {
			System.out.println("--查询数据出现异常："+sql);
			e.printStackTrace();
		}finally{
			close();
		}
		return list;
	}
	
	
	/**
	 * 删除内容
	 * @param conditions	条件
	 * @return 响应条数
	 */
	public int delete(String conditions) {
		StringBuffer sql = new StringBuffer("delete from " + tableName + " ");
		try {
			if (conditions != null && !"".equals(conditions)) {
				sql.append("where " + conditions);
			}
			if(conn.isClosed())conn=new DBconn().getConn(); 	//获取数据库连接
			pstm = conn.prepareStatement(sql.toString());
			return pstm.executeUpdate();
		} catch (Exception e) {
			System.out.println("--删除数据出现异常："+sql);
			e.printStackTrace();
		}finally{
			close();
		}
		return 0;
	}
	
	/**
	 * 保存 实体
	 * @param entity	实体
	 * @return 是否成功
	 */
	public boolean save(T entity) {
		try {
			StringBuffer sql = new StringBuffer();
			StringBuffer value = new StringBuffer();
			sql.append("insert into "+tableName+" (");
			for (Field field : attr) {
				field.setAccessible(true); // 设置属性为可访问
				if(!field.isAnnotationPresent(NoDB.class) && !field.getName().equals("serialVersionUID")){	//判断该字段是否参与操作
					String val = field.get(entity) + "";	//获取值
					if (!"null".equals(val) && !"0".equals(val)) { // 如果值不为空
						sql.append(field.getName() + ",");
						value.append("'" + field.get(entity) + "',");
					}
				}
			}
			sql.deleteCharAt(sql.length() - 1); // 去掉最後一個逗號
			value.deleteCharAt(value.length() - 1); // 去掉最後一個逗號
			sql.append(")values("+value.toString()+")");	//把 值 加入 sql语句
			
			if(conn.isClosed())conn=new DBconn().getConn(); 	//获取数据库连接
			//执行操作
			pstm=conn.prepareStatement(sql.toString());
			int rs = pstm.executeUpdate();	
			if(rs>0)return true;
		} catch (Exception e) {
			System.out.println("--执行保存语句出现异常。");
			e.printStackTrace();
		}finally{
			close();
		}
		return false;
	}
	
	/**
	 * 自动创表
	 */
	private void createTable(){
		StringBuffer sql = new StringBuffer();
		if(!"".equals(tableName)){
			try {
				//判断该表是否存在
				DatabaseMetaData meta = conn.getMetaData();
				ResultSet rsTables = meta.getTables(null , null,tableName, null);
				if(!rsTables.next()){
				   //创建表
					sql.append("create table "+tableName + " ( ");
					for (Field field : attr) {
						if(!field.isAnnotationPresent(NoDB.class) && !field.getName().equals("serialVersionUID")){	//判断该字段是否创建数据库
							String type = javaType2SQLType(field.getType().getName());	//获取当前的数据类型
							if(field.getName().equals(tableId))sql.append(field.getName() + " " + type + " auto_increment primary key,");
							else{
								String defaultValue="default ''";
								if("int".equals(type) || "float".equals(type) || "bigint".equals(type) || "date".equals(type))defaultValue="default 0";			//当前的默认值
								sql.append(field.getName() + " " + type + " not null "+defaultValue+",");
							}
						}
					}
					sql.deleteCharAt(sql.length() - 1); // 去掉最後一個逗號
					sql.append(")");

					if(conn.isClosed())conn=new DBconn().getConn(); 	//获取数据库连接
					pstm=conn.prepareStatement(sql.toString());
					pstm.executeUpdate();				//执行操作
					System.out.println("--自動創表：" + tableName);
				}
			} catch (Exception e) {
				System.out.println("--创表出现异常"+tableName+"."+sql);
				e.printStackTrace();
			}finally{
				close();
			}
		}
	}

	
	/**
	 * Java数据类型 转换为 SQL数据类型
	 * @param type
	 * @return
	 */
	private String javaType2SQLType(String type){
		//int、boolean、float、double、char 不改变
		switch (type) {
		case "java.lang.String":		//字符串
			type="varchar(255)";
			break;
		case "long":
			type="bigint";
			break;
		case "java.sql.Date":					//二进制
			type="date";		
			break;
		case "java.sql.Time":					//二进制
			type="time";		
			break;
		case "java.sql.Timestamp":					//二进制
			type="dateTime";		
			break;
		}

		return type;
	}
	
	/**
	 * 获取 实体类的 ID
	 * @return
	 */
	private String getTableId(){
		for (Field field : attr) {
			if(field.isAnnotationPresent(Id.class)){
				tableId = field.getName();
			}
		}
		return tableId;
	}
	
	/**
	 * 获取 实体所对应的 表名
	 * @param entity
	 * @return
	 */
	private String getTableName(){
		//判断该 类是否已备注 实体
		if(clazz.isAnnotationPresent(Table.class)){
			Table table = (Table)clazz.getAnnotation(Table.class);
			tableName = table.value();
		}else{
			System.out.println("--"+clazz.getName()+"未加上 @Table 注解。");
		}
		return tableName;
	}
	
	/**
	 * 关闭数据库
	 */
	private void  close(){
		try {
			if(rs!=null)rs.close();
			if(pstm!=null)pstm.close();
			if(conn!=null)conn.close();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
}
